
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "Socket.h"
#include "dump.h"


#define DHCP_DISCOVER    1
#define DHCP_OFFER       2
#define DHCP_REQUEST     3
#define DHCP_ACK         5
#define DHCP_NAK         6

#pragma pack(1)
typedef struct
{
    unsigned char   uMessageType;
    unsigned char   uHardwareType;
    unsigned char   uHAddressLength;
    unsigned char   uHops;
    unsigned int    xid;
    unsigned short  uSecondsElapsed;
    unsigned short  uBootpFlags;
    unsigned char   ClientIP[4];
    unsigned char   YourIP[4];
    unsigned char   NextServer[4];
    unsigned char   RelayAgent[4];
    unsigned char   ClientMac[6];
    unsigned char   padding[10];
    unsigned char   ServerHostName[64];
    unsigned char   BootFileName[128];
    unsigned int    MagicCookie;
    unsigned char   options[308];
}DHCPDATA;

typedef struct {
    unsigned char code;
    unsigned char len;
    char          value[1024];
} OPT56;


int addOption(void* out, int opt, const void* buf, int len)
{
    memcpy(out, &opt, 1);
    if (len != 0) {
        memcpy((char*)out + 1, &len, 1);
        memcpy((char*)out + 2, buf, len);
    }
    return len + 2;
}

void getOption(DHCPDATA& packet, int opt, void* buf, int len)
{
    unsigned char * p = packet.options;
    while (*p != (unsigned char)opt && *p != 0xff) {
        p += *(p + 1) + 2;
    }
    if (*p == 0xff) {
        memset(buf, 0, len);
        return;
    }
    if (len > *(p + 1)) {
        len = *(p + 1);
    }
    memcpy(buf, p + 2, len);
}


void ParsePack(DHCPDATA& packet, int xid)
{

    char    mac[100];
    memcpy(mac, packet.ClientMac, 6);

    char    opt53[100];
    getOption(packet, 53, opt53, sizeof(opt53));

    char    reply[100] = {0};
    if (opt53[0] == DHCP_DISCOVER) {
        reply[0] = DHCP_OFFER;
    } else if (opt53[0] == DHCP_REQUEST) {
        reply[0] = DHCP_ACK;
    }

    memset(&packet, 0, sizeof(packet));
    packet.uMessageType     = 2;    // Reply;
    packet.uHardwareType    = 1;    // Ethernet;
    packet.uHAddressLength  = 6;
    packet.uHops            = 0;
    packet.xid              = xid;
    packet.uSecondsElapsed  = 0;
    packet.uBootpFlags      = 0;    // Unicast
    packet.YourIP[0]        = 10;
    packet.YourIP[1]        = 0;
    packet.YourIP[2]        = 0;
    packet.YourIP[3]        = 188;
    packet.MagicCookie      = 0x63538263;
    memcpy(packet.ClientMac, mac, 6);

    int offset = 0;
    offset += addOption(packet.options + offset, 53, reply, 1);

#if 0

    char    opt56[4096] = {0};
    int     len = 0;

    OPT56   option;
    option.code = 1;
    option.len = 4;
    int err = 1234;
    memcpy(option.value, &err, 4);
    memcpy(opt56, &option, option.len + 2);
    len = option.len + 2;

    const char * message = "this is an error message...";
    option.code = 2;
    option.len = strlen(message);
    strcpy(option.value, message);
    memcpy(opt56 + len, &option, option.len + 2);
    len += option.len + 2;

    const char * dhcpuser = "DHCPUSERNAME";
    option.code = 3;
    option.len = strlen(dhcpuser);
    strcpy(option.value, dhcpuser);
    memcpy(opt56 + len, &option, option.len + 2);
    len += option.len + 2;

    offset += addOption(packet.options + offset, 56, opt56, len);
#endif

    offset += addOption(packet.options + offset, 255, NULL, 0);

    /*
    FILE *  fp = fopen("data.bin", "rb");
    char    buffer[1024];
    int     ret;
    fseek(fp, 240, SEEK_SET);
    ret = fread(buffer, 1, 1024, fp);
    printf("%d bytes read.\n", ret);
    fclose(fp);
    memcpy(packet.options, buffer, ret);
    */

}

int main()
{
    CSocket::InitNetwork();

    CSocket         sock(CSocket::eSocketType_UDP);
    sock.Create();
    sock.SetBroadCast(true);

    DHCPDATA        packet;
    try
    {
        sock.SetHostIP("0.0.0.0");
        sock.Bind(67);
        while(1)
        {
            int ret = sock.Recv(&packet, sizeof(packet));
            printf("recv %d bytes.\n", ret);
            dump(&packet, sizeof(packet));
            ParsePack(packet, packet.xid);
            sock.SendTo("255.255.255.255", 68, &packet, sizeof(packet));
        }
    } catch(const char * e) {
        printf("%s\n", e);
    }
    return 0;
}

